package hardware;

import java.util.LinkedList;

public class Issue {

	static LinkedList<Instruction> instructions = new LinkedList<Instruction>();

	/**
	 * add instruction to a functional unit if there is a functional unit of
	 * it's type.
	 * 
	 * @param ins
	 *            instruction i want to add in the functional unit.
	 * @return true if it's successfully added to functional unit.
	 */
	public boolean addInFU(Instruction ins) {
		String type;
		if (ins.getType().equals(Instruction.ADD)
				|| ins.getType().equals(Instruction.ADDI)
				|| ins.getType().equals(Instruction.NAND)) {
			type = Instruction.ADD;
		} else {
			type = ins.getType();
		}
		if (!ins.getType().equals(Instruction.BRANCH_IF_EQUAL)
				&& !ins.getType().equals(Instruction.LOAD_WORD)
				&& !ins.getType().equals(Instruction.STORE_WORD)
				&& !ins.getType().equals(Instruction.RETURN)
				&& !ins.getType().equals(Instruction.JUMP)
				&& !ins.getType().equals(Instruction.JUMP_AND_LINK)) {
			for (int i = 0; i < Processor.functionalUnits.size(); i++) {
				if (!Processor.functionalUnits.get(i).reserved
						&& Processor.functionalUnits.get(i).type.equals(type)) {
					Processor.functionalUnits.get(i).add();
					Processor.functionalUnits.get(i).id = ins.getID();
					return true;
				}
			}
		} else {
			return true;
		}
		return false;
	}

	/**
	 * adds instructions to execution stage.
	 * 
	 * @param currentTime
	 *            the current time at which the operation is performed.
	 */
	public static void issue(Long currentTime) {
		for (int i = 0; i < instructions.size(); i++) {
			if (Execute.ins.size() < Processor.m) {
				Instruction instruction = instructions.remove(i);
				i--;
				instruction.setIssueEndTime(currentTime);
				Execute.addInstructionToExecuteStage(instruction, currentTime);
			}
		}
	}

	/**
	 * adds new instructions to new stage.
	 * 
	 * @param ins
	 *            instruction that i want to add to issue stage.
	 * @param currentTime
	 *            the current time at which the operation is performed.
	 */
	public static void addInstructionToIssueStage(Instruction ins,
			Long currentTime) {
		ins.setIssueStartTime(currentTime);
		instructions.add(ins);
	}

	/**
	 * handles the new instructions coming to issue.
	 * @param instruction
	 *                   instruction that just come now to issue.
	 * @param currentTime
	 *                   the current time at which the operation is performed.
	 */
	public static void issueNew(Instruction instruction, long currentTime) {
		addInstructionToIssueStage(instruction, currentTime);
	}

	public static void removeInstructionsAfterId(int id) {
		LinkedList<Instruction> ins = new LinkedList<Instruction>();
		for (int i = 0; i < instructions.size(); i++) {
			if (instructions.get(i).getID() > id) {
				ins.add(instructions.get(i));
			}
		}
		instructions.removeAll(ins);
	}

	public static void removeInstructionsBeforeId(int id) {
		LinkedList<Instruction> ins = new LinkedList<Instruction>();
		for (int i = 0; i < instructions.size(); i++) {
			if (instructions.get(i).getID() < id) {
				ins.add(instructions.get(i));
			}
		}
		instructions.removeAll(ins);
	}

}
